package com.puzzle;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.view.View;
import android.widget.AbsoluteLayout;

import java.util.Random;

// 游戏
class Game implements Callback {
    // 难度
    public enum Difficulty {
        EASY(9),    // 简单
        MEDIUM(25), // 中等
        HARD(36);   // 困难

        /**
         * 构造方法。
         * @param value 枚举值
         */
        Difficulty(int value) {
            _value = value;
        }

        /**
         * 获取枚举值。
         * @return 枚举值
         */
        public int value() {
            return _value;
        }
        
        private final int _value;   // 枚举值
    }

    // 状态
    public enum Status {
        END,    // 已结束
        PLAYING // 游戏中
    }

    private final Handler mCallback;            // 回调
    private Difficulty mDifficulty;             // 难度
    private final Rect mEmptyAreaRect;          // 空白区域矩形
    private Point mEmptyPoint;                  // 空点，即没有图的那个点
    private final AbsoluteLayout mGameBoard;    // 游戏区域
    private final Bitmap mImage;                // 图片
    private final Paint mPaint;                 // 绘图相关信息
    private boolean mPicHasBorder;              // 图片控件是否有边框标志
    public PictureCtrl[] mPictures;             // 图片控件数组
    private final Random mRandom;               // 随机数生成器
    private Status mStatus;                     // 状态

    /**
     * 构造方法。
     * @param callback  回调
     * @param gameBoard 游戏区域
     */
    public Game(Callback callback, AbsoluteLayout gameBoard) {
        // 初始化成员
        mCallback = new Handler(callback);
        mDifficulty = Difficulty.EASY;
        mGameBoard = gameBoard;
        mImage = Bitmap.createBitmap(gameBoard.getWidth(), gameBoard.getHeight(), Bitmap.Config.ARGB_8888);
        mPaint = new Paint();
        mPicHasBorder = true;
        mRandom = new Random();
        mStatus = Status.END;

        // 设置空白区域的位置和尺寸
        final int gameBoardWidth = mGameBoard.getWidth();
        final int gameBoardHeight = mGameBoard.getHeight();
        final int diffBaseMedium = (int)Math.sqrt(Difficulty.MEDIUM.value());
        final int diffBaseHard = (int)Math.sqrt(Difficulty.HARD.value());
        int emptyAreaSize = gameBoardHeight;
        do {
            emptyAreaSize--;
        } while (emptyAreaSize % diffBaseMedium != 0 || emptyAreaSize % diffBaseHard != 0);
        final int emptyAreaLeft = (gameBoardWidth - emptyAreaSize) / 2;
        final int emptyAreaTop = (gameBoardHeight - emptyAreaSize) / 2;
        mEmptyAreaRect = new Rect(emptyAreaLeft, emptyAreaTop, emptyAreaLeft + emptyAreaSize, emptyAreaTop + emptyAreaSize);

        // 设置图片
        this.setPicture(BitmapFactory.decodeResource(mGameBoard.getResources(), R.mipmap.pic));
    }

    /**
     * 创建所有图片控件。
     */
    private void createPictures() {
        int left, top;                                              // 图片控件位置
        final int baseCount = (int)Math.sqrt(mDifficulty.value());  // 一行（列）的图片控件数
        final int size = mEmptyAreaRect.width() / baseCount;        // 图片控件尺寸

        // 删除所有图片控件
        this.deletePictures();

        // 创建所有图片控件
        mPictures = new PictureCtrl[mDifficulty.value()]; // 图片控件数组
        int i, j = 0;
        for (i = 0; i < mDifficulty.value(); i++) {
            if (i % baseCount == 0 && i != 0) { // 如果一行满了，换到下一列
                j++;
            }

            // 计算左上角坐标
            left = mEmptyAreaRect.left + size * (i % baseCount);
            top = mEmptyAreaRect.top + size * j;

            // 创建图片控件
            mPictures[i] = new PictureCtrl(this, mGameBoard.getContext(), mPicHasBorder, mImage, size, left, top);
            final AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams(size, size, left, top);
            mGameBoard.addView(mPictures[i], params);
        }
    }

    /**
     * 删除所有图片控件。
     */
    private void deletePictures() {
        mGameBoard.removeAllViews();
    }

    /**
     * 游戏结束。
     */
    private void gameOver() {
        // 空白图片控件显示图片
        mPictures[mDifficulty.value() - 1].setVisibility(View.VISIBLE);

        // 将状态设置为已结束
        mStatus = Status.END;

        // 发送通知
        mCallback.sendEmptyMessage(Const.UM_GAME_OVER);
    }

    /**
     * 获取游戏难度。
     * @return 游戏难度
     */
    public Difficulty getDifficulty() {
        return mDifficulty;
    }

    /**
     * 获取游戏状态。
     * @return 游戏状态
     */
    public Status getStatus() {
        return mStatus;
    }

    /**
     * 自定义事件的响应方法。
     * @param message   事件
     * @return 事件在此被处理则返回 true，否则返回 false
     */
    @Override
    public boolean handleMessage(Message message) {
        if (message.what == Const.UM_PICTURE_CTRL_CLICKED) {
            this.movePicture((PictureCtrl)message.obj);
            return true;
        }
        return false;
    }

    /**
     * 判断是否胜利。
     * @return 胜利则返回 true，否则返回 false
     */
    private boolean isWon() {
        boolean result = false;
        for (int i = 0; i < mDifficulty.value(); i++) {
            result = mPictures[i].isOnOriginalPosition();
            if (!result) {
                break;
            }
        }
        return result;
    }

    /**
     * 移动图片控件。
     * @param pictureCtrl   图片控件
     */
    private void movePicture(PictureCtrl pictureCtrl) {
        if (mStatus != Status.PLAYING) {
            return;
        }

        final Point position = new Point(pictureCtrl.getPosition());

        // 如果要移动的图片控件与空点相邻，则移动
        if (position.y == mEmptyPoint.y && Math.abs(position.x - mEmptyPoint.x) == pictureCtrl.getWidth() ||
                position.x == mEmptyPoint.x && Math.abs(position.y - mEmptyPoint.y) == pictureCtrl.getHeight()) {
            pictureCtrl.setPosition(mEmptyPoint);
            mEmptyPoint = position;

            // 判断是否胜利
            if (this.isWon()) {
                this.gameOver();
            }
        }
    }

    /**
     * 获取图片控件是否有边框。
     * @return 有边框则为 true，否则为 false
     */
    public boolean pictureHasBorder() {
        return mPicHasBorder;
    }

    /**
     * 设置游戏难度。
     * @param diff  游戏难度
     */
    public void setDifficulty(Difficulty diff) {
        // 设置难度
        mDifficulty = diff;

        // 重新创建所有图片
        this.createPictures();

        // 将状态设置为已结束
        mStatus = Status.END;
    }

    /**
     * 设置图片。
     * @param image 图片
     */
    public void setPicture(Bitmap image) {
        // 将图片画到游戏区域上
        final Canvas drawer = new Canvas(mImage);
        drawer.drawBitmap(image,
            new Rect(0, 0, image.getWidth(), image.getHeight()),
            new Rect(0, 0, mImage.getWidth(), mImage.getHeight()), mPaint);

        // 画中间的空白区域
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Const.EMPTY_AREA_COLOR);
        final Bitmap backgroundImage = mImage.copy(Bitmap.Config.ARGB_8888, true);
        new Canvas(backgroundImage).drawRect(mEmptyAreaRect, mPaint);

        // 将图片设置到游戏区域上
        final Drawable drawable = new BitmapDrawable(backgroundImage);
        mGameBoard.setBackground(drawable);

        // 创建所有图片
        this.createPictures();

        // 将状态设置为已结束
        mStatus = Status.END;
    }

    /**
     * 设置图片控件是否有边框。
     * @param hasBorder 有边框则为 true，否则为 false
     */
    public void setPictureBorder(boolean hasBorder) {
        mPicHasBorder = hasBorder;

        // 设置所有图片控件的边框
        for (int i = 0; i < mDifficulty.value(); i++) {
            mPictures[i].setBorder(hasBorder);
        }
    }

    /**
     * 游戏开始。
     */
    public void start() {
        // 重新创建所有图片
        if (mStatus != Status.END) {
            this.createPictures();
        }

        // 重置成员变量
        final int baseCount = (int)Math.sqrt(mDifficulty.value());
        final int size = mPictures[0].getWidth();
        mEmptyPoint = new Point(mEmptyAreaRect.left + size * baseCount, mEmptyAreaRect.top + size * (baseCount - 1));
        mStatus = Status.PLAYING;

        // 右下角图片控件右移
        final Point position = new Point(mPictures[mDifficulty.value() - 1].getPosition());
        mPictures[mDifficulty.value() - 1].setPosition(mEmptyPoint);
        mEmptyPoint = position;

        // 乱序其它图片控件的位置
        int index1, index2;
        for (int i = 0; i < mDifficulty.value() - 1; i++) {
            do {
                index1 = mRandom.nextInt(mDifficulty.value() - 1);
                index2 = mRandom.nextInt(mDifficulty.value() - 1);
            } while (index1 == index2);
            this.swapPictures(index1, index2);
        }
    }

    /**
     * 交换两个图片控件的位置。
     * @param index1    图片控件1的索引
     * @param index2    图片控件2的索引
     */
    private void swapPictures(int index1, int index2) {
        final Point position1 = new Point(mPictures[index1].getPosition());
        final Point position2 = new Point(mPictures[index2].getPosition());
        mPictures[index1].setPosition(position2);
        mPictures[index2].setPosition(position1);
    }
}
